package com.code.monitor.resolve;

import com.alibaba.fastjson2.JSON;
import com.code.monitor.annotation.*;
import com.code.monitor.constants.ResultCode;
import com.code.monitor.constants.ResultEntity;
import com.code.monitor.model.MethodBean;
import io.netty.handler.codec.http.multipart.FileUpload;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.system.ApplicationHome;
import org.springframework.core.io.ClassPathResource;

import java.io.File;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * @author ggh
 * @date 2023/5/8
 */
public class MethodReslover {
    private static final String rootPath = "/classes/";
    private static final String controllerPath = "com/code/monitor/controller";
    private static final Logger logger = LoggerFactory.getLogger(MethodReslover.class);

    public static void init() {
        try {
            URL url = MethodReslover.class.getClassLoader().getResource("");
            List<String> controllerClassList = new ArrayList<>();

            //判断是否是jar包环境
            if ("jar".equals(url.getProtocol())) {
                ApplicationHome applicationHome = new ApplicationHome(MethodReslover.class);
                logger.info(applicationHome.getSource().getAbsolutePath());
                JarFile jarFile = new JarFile(applicationHome.getSource().getAbsolutePath());
                Enumeration<JarEntry> entry = jarFile.entries();

                while (entry.hasMoreElements()) {
                    JarEntry jarEntry = entry.nextElement();
                    if (!jarEntry.isDirectory() && jarEntry.getName().contains(controllerPath)) {
                        String simpleName = jarEntry.getName().substring(jarEntry.getName().lastIndexOf("/") + 1, jarEntry.getName().lastIndexOf("."));
                        controllerClassList.add(simpleName);
                    }
                }
            } else {
                File classFile = new ClassPathResource("/").getFile().getParentFile();
                Path controllerDir = Paths.get(classFile.getAbsolutePath(), rootPath, controllerPath);
                for (String subFile : controllerDir.toFile().list()) {
                    controllerClassList.add(subFile.substring(0, subFile.lastIndexOf(".")));
                }
            }

            String controllerClassPath = controllerPath.replace("/", ".");
            for (String controllerClass : controllerClassList) {
                Class cls = Class.forName(controllerClassPath + "." + controllerClass);
                if (cls.getDeclaredAnnotation(Controller.class) == null && cls.getDeclaredAnnotation(ControllerAdvice.class) == null) {
                    continue;
                }
                if (cls.getDeclaredAnnotation(Controller.class) != null) {
                    Object obj = cls.newInstance();
                    Method[] methods = cls.getDeclaredMethods();
                    for (Method method : methods) {
                        if (method.getAnnotation(RequestMapping.class) == null) {
                            continue;
                        }
                        RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
                        String uri = requestMapping.value();

                        method.getGenericParameterTypes();
                        MethodBean methodBean = new MethodBean();
                        methodBean.setMethod(method);
                        methodBean.setObj(obj);
                        UriMappingReslover.addUriMapping(uri, methodBean);
                    }
                } else if (cls.getDeclaredAnnotation(ControllerAdvice.class) != null) {
                    Object obj = cls.newInstance();
                    Method[] methods = cls.getDeclaredMethods();
                    for (Method method : methods) {
                        if (method.getAnnotation(ExceptionHandler.class) == null) {
                            continue;
                        }
                        ExceptionHandler exceptionHandler = method.getAnnotation(ExceptionHandler.class);
                        Class exCls = exceptionHandler.value();

                        method.getGenericParameterTypes();
                        MethodBean methodBean = new MethodBean();
                        methodBean.setMethod(method);
                        methodBean.setObj(obj);
                        UriMappingReslover.addExMappings(exCls, methodBean);
                    }
                }
            }
            UriMappingReslover.print();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static Object invoke(MethodBean methodBean, Object... args) {
        Object result = null;
        try {
            Method method = methodBean.getMethod();
            Parameter[] parameters = method.getParameters();
            Object[] realArgs = new Object[parameters.length];
            for (int i = 0; i < parameters.length; i++) {
                if (parameters[i] == null) {
                    break;
                }

                if (parameters[i].getAnnotation(RequestBody.class) != null && args[i] instanceof String) {
                    realArgs[i] = JSON.parseObject((String) args[i], parameters[i].getType());
                }
                if (parameters[i].getAnnotation(RequestParam.class) != null) {
                    Map<String, Object> params = (Map<String, Object>) args[0];
                    realArgs[i] = params.get(parameters[i].getAnnotation(RequestParam.class).value());

                    if (!parameters[i].getAnnotation(RequestParam.class).require() && realArgs[i] == null) {
                        realArgs[i] = null;
                        continue;
                    }
                    if (parameters[i].getAnnotation(RequestParam.class).require() && realArgs[i] == null) {
                        return JSON.toJSONString(ResultEntity.fail(ResultCode.PARAM_LACK.getCode(), null, parameters[i].getName() + ",参数缺失"));
                    }
                    Class cls = parameters[i].getType();
                    if (!(parameters[i].getType().equals(String.class))) {
                        if (!(cls.isAssignableFrom(realArgs[i].getClass()))) {
                            if (cls.isAssignableFrom(FileUpload.class) && "undefined".equalsIgnoreCase((String) realArgs[i])) {
                                return JSON.toJSONString(ResultEntity.fail(ResultCode.PARAM_LACK.getCode(), null, parameters[i].getName() + ",参数缺失"));
                            }
                            realArgs[i] = JSON.parseObject((String) realArgs[i], parameters[i].getType());
                        }
                    } else {
                        realArgs[i] = URLDecoder.decode((String) realArgs[i]);
                    }
                }
            }

            result = methodBean.getMethod().invoke(methodBean.getObj(), realArgs);
            if (method.getAnnotation(ResponseBody.class) != null) {
                result = JSON.toJSONString(result);
            }
        } catch (Exception e) {
            e.printStackTrace();
            MethodBean exMethodBean = UriMappingReslover.getExMappings(e.getClass());
            Object obj = null;
            try {
                obj = exMethodBean.getMethod().invoke(methodBean.getObj(), e);
            } catch (Exception exception) {
                exception.printStackTrace();
            }
            if (!(obj instanceof String)) {
                result = JSON.toJSONString(obj);
            }
        }

        return result;
    }
}
